package com.dituhui.tool.gis;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import com.dituhui.tool.util.StringUtil;
import com.supermap.data.GeoLine;
import com.supermap.data.GeoRegion;
import com.supermap.data.Geometrist;
import com.supermap.data.Point2D;
import com.supermap.data.Point2Ds;

/**
 * 面对象
 * 
 * @author liweigu
 * 
 */
public class Region {
	private static Logger LOG = Logger.getLogger(Region.class);

	private List<Point> points = new ArrayList<Point>();
	private int[] parts;

	/**
	 * 获取点串
	 * 
	 * @return 点串
	 */
	public List<Point> getPoints() {
		return points;
	}

	/**
	 * 设置点串
	 * 
	 * @param points 点串
	 */
	public void setPoints(List<Point> points) {
		this.points = points;
	}

	/**
	 * 获取点串分组
	 * 
	 * @return 点串分组
	 */
	public int[] getParts() {
		return parts;
	}

	/**
	 * 设置点串分组。“岛”、“洞”多边形是由多个部分组成的，parts的长度代表有几个部分，parts数组成员的数字代表各部分的点串个数；如果parts为null，则代表多边形不是“岛”、“洞”，即只有一个部分。
	 * 
	 * @param parts 点串分组
	 */
	public void setParts(int[] parts) {
		this.parts = parts;
	}

	/**
	 * 构造函数
	 * 
	 * @param points 点串。points应包含至少4个点，最后一个点必须跟第一个点重合。
	 */
	public Region(List<Point> points) {
		if (points == null || points.size() < 4) {
			throw new IllegalArgumentException("points必须包含至少4个点");
		} else if (points.get(0) == null) {
			throw new IllegalArgumentException("points的第1个点为空");
		} else if (!points.get(0).equals(points.get(points.size() - 1))) {
			throw new IllegalArgumentException("points的第1个点跟最后1个（第" + (points.size() - 1) + "）点不重合");
		}
		this.setPoints(points);
	}

	/**
	 * 构造函数
	 * 
	 * @param points 点串。points应包含至少4个点，最后一个点必须跟第一个点重合。
	 * @param parts 点串分组
	 */
	public Region(List<Point> points, int[] parts) {
		if (points == null || points.size() < 4) {
			throw new IllegalArgumentException("points必须包含至少4个点");
		} else if (points.get(0) == null) {
			throw new IllegalArgumentException("points的第1个点为空");
		} else if (parts == null && !points.get(0).equals(points.get(points.size() - 1))) {
			throw new IllegalArgumentException("points的第1个点跟最后1个（第" + (points.size() - 1) + "）点不重合");
		}
		this.setParts(parts);
		this.setPoints(points);
	}

	@Override
	/**
	 * 转换为字符串。字符串内容为： 格式一：“x1,y1;x2,y2...”。说明：如果点的个数小于3则返回空字符串。多边形只有一个部分。 格式二：“10,24^x1,y1;x2,y2...”。说明：^前面的数字串定义多个部分组成（parts）。
	 * 
	 * @return 字符串
	 */
	public String toString() {
		String s;
		if (this.points == null || this.points.size() < 3) {
			s = "";
		} else {
			StringBuilder stringBuilder = new StringBuilder();
			if (this.parts != null && this.parts.length > 0) {
				stringBuilder.append(this.parts[0]);
				for (int i = 1; i < this.parts.length; i++) {
					stringBuilder.append(StringUtil.COMMA);
					stringBuilder.append(this.parts[i]);
				}
				stringBuilder.append(StringUtil.AngularBracket);
			}
			for (Point point : this.points) {
				stringBuilder.append(point == null ? StringUtil.EMPTY : point.toString());
				stringBuilder.append(StringUtil.SEMICOLON);
			}
			if (stringBuilder.length() > 1) {
				// 删除最后一个分号
				stringBuilder.deleteCharAt(stringBuilder.length() - 1);
			}
			s = stringBuilder.toString();
		}
		return s;
	}

	/**
	 * 转换为字符串。字符串内容为： 格式一：“x1,y1;x2,y2...”。说明：如果点的个数小于3则返回空字符串。多边形只有一个部分。 格式二：“10,24^x1,y1;x2,y2...”。说明：^前面的数字串定义多个部分组成（parts）。
	 * 
	 * @param s 字符串
	 * @return 面对象
	 */
	public static Region parseRegion(String s) {
		Region region = null;

		// 字符串要包含SEMICOLON
		if (s != null && s.contains(StringUtil.SEMICOLON)) {
			String strRegion;
			// 解析parts
			int[] parts = null;
			if (s.contains(StringUtil.AngularBracket)) {
				String[] arr = s.split(StringUtil.AngularBracketForSplit);
				String strParts = arr[0];
				// parts字符串要包含COMMA
				if (strParts.contains(StringUtil.COMMA)) {
					String[] arr2 = strParts.split(StringUtil.COMMA);
					parts = new int[arr2.length];
					int i = 0;
					for (String s2 : arr2) {
						int partCount = Integer.parseInt(s2);
						parts[i++] = partCount;
					}
				}
				// AngularBracket之后的部分
				strRegion = arr[1];
			} else {
				// 没有parts的内容
				strRegion = s;
			}
			String[] arr = strRegion.split(StringUtil.SEMICOLON);
			List<Point> points = new ArrayList<Point>();
			for (String strPoint : arr) {
				if (strPoint != null && strPoint.contains(StringUtil.COMMA)) {
					String[] arr2 = strPoint.split(StringUtil.COMMA);
					String strX = arr2[0];
					String strY = arr2[1];
					double x = Double.parseDouble(strX);
					double y = Double.parseDouble(strY);
					Point point = new Point(x, y);
					points.add(point);
				}
			}
			// if (points.size() > 3) {
			// // 最后一个点要跟第一个点重合
			// points.add(points.get(0));
			// }
			region = new Region(points, parts);
		}

		return region;
	}

	/**
	 * 线拆分。依赖SuperMap Object Java。
	 * 
	 * @param line 线对象。必须包含至少2个点。
	 * @return 拆分后的面对象数组
	 */
	public Region[] lineSplit(Line line) {
		if (line == null || line.getPoints() == null || line.getPoints().size() < 2) {
			throw new IllegalArgumentException("line必须包含至少2个点");
		}
		Point2Ds linePoint2Ds = new Point2Ds();
		for (Point pointInLine : line.getPoints()) {
			if (pointInLine != null) {
				linePoint2Ds.add(new Point2D(pointInLine.x, pointInLine.y));
			} else {
				throw new IllegalArgumentException("line包含了空点");
			}
		}
		GeoLine splitLine = new GeoLine(linePoint2Ds);
		GeoRegion target1 = new GeoRegion(), target2 = new GeoRegion();
		GeoRegion region = this.toGeoRegion();
		boolean success = Geometrist.splitRegion(region, splitLine, target1, target2);
		if (success) {
			Region region1 = Region.fromGeoRegion(target1);
			Region region2 = Region.fromGeoRegion(target2);
			return new Region[] { region1, region2 };
		} else {
			LOG.warn("Geometrist.splitRegion返回false");
			return null;
		}
	}

	/**
	 * 面合并。依赖SuperMap Object Java。
	 * 
	 * @param region 被合并的面对象。它必须跟自身相邻，否则返回null。
	 * @return 合并后的面对象
	 */
	public Region union(Region region) {
		Region newRegion = null;

		GeoRegion region1 = this.toGeoRegion();
		GeoRegion region2 = region.toGeoRegion();
		// 检查两个面是否相邻
		if (Geometrist.hasTouch(region1, region2)) {
			GeoRegion newGeoRegion = (GeoRegion) Geometrist.union(region1, region2);
			newRegion = Region.fromGeoRegion(newGeoRegion);
		}

		return newRegion;
	}

	/**
	 * 面擦除。依赖SuperMap Object Java。
	 * 
	 * @param region 要擦除的面对象。
	 * @return 擦除后的面对象
	 */
	public Region erase(Region region) {
		Region newRegion;

		GeoRegion region1 = this.toGeoRegion();
		GeoRegion region2 = region.toGeoRegion();
		// 擦除
		GeoRegion newGeoRegion = (GeoRegion) Geometrist.erase(region1, region2);
		newRegion = Region.fromGeoRegion(newGeoRegion);

		return newRegion;
	}

	/**
	 * 点是否在面中
	 * 
	 * @param point 点
	 * @return 是否
	 */
	public boolean contains(Point point) {
		if (point == null) {
			return false;
		}
		java.awt.geom.GeneralPath generalPath = new java.awt.geom.GeneralPath();
		boolean isFirst = true;
		for (Point p : this.points) {
			if (isFirst) {
				generalPath.moveTo(p.x, p.y);
				isFirst = false;
			} else {
				generalPath.lineTo(p.x, p.y);
			}
		}
		generalPath.lineTo(this.points.get(0).x, this.points.get(0).y);
		generalPath.closePath();
		return generalPath.contains(new java.awt.geom.Point2D.Double(point.x, point.y));
	}

	// 转换对象
	private static Region fromGeoRegion(GeoRegion geoRegion) {
		Region region = null;

		if (geoRegion == null || geoRegion.getPartCount() == 0) {
			throw new IllegalArgumentException("region为空");
		} else {
			Point2Ds point2Ds = geoRegion.getPart(0);
			int count = point2Ds.getCount();
			List<Point> points = new ArrayList<Point>();
			for (int i = 0; i < count; i++) {
				Point2D point2D = point2Ds.getItem(i);
				Point point = new Point(point2D.getX(), point2D.getY());
				points.add(point);
			}
			region = new Region(points);
		}

		return region;
	}

	private GeoRegion toGeoRegion() {
		GeoRegion geoRegion = new GeoRegion();

		Point2Ds point2Ds = new Point2Ds();
		for (Point point : this.points) {
			if (point != null) {
				point2Ds.add(new Point2D(point.x, point.y));
			} else {
				throw new IllegalArgumentException("this.points 包含了空点");
			}
		}
		geoRegion.addPart(point2Ds);

		return geoRegion;
	}

	public static void main(String[] args) {
		String test = "erase";
		if ("lineSplit".equals(test)) {
			List<Point> regionPoints = new ArrayList<Point>();
			regionPoints.add(new Point(0, 0));
			regionPoints.add(new Point(10, 0));
			regionPoints.add(new Point(10, 10));
			regionPoints.add(new Point(0, 10));
			regionPoints.add(new Point(0, 0));
			Region region = new Region(regionPoints);
			List<Point> linePoints = new ArrayList<Point>();
			linePoints.add(new Point(0, 0));
			linePoints.add(new Point(10, 10));
			Line line = new Line(linePoints);

			Region[] plittedRegions = region.lineSplit(line);
			for (Region plittedRegion : plittedRegions) {
				System.out.println(plittedRegion);
			}
		} else if ("union".equals(test)) {
			// 面A
			List<Point> regionPointsA = new ArrayList<Point>();
			regionPointsA.add(new Point(0, 0));
			regionPointsA.add(new Point(10, 0));
			regionPointsA.add(new Point(10, 10));
			regionPointsA.add(new Point(0, 10));
			regionPointsA.add(new Point(0, 0));
			Region regionA = new Region(regionPointsA);
			System.out.println(regionA);
			// 面B
			List<Point> regionPointsB = new ArrayList<Point>();
			regionPointsB.add(new Point(0, 0));
			regionPointsB.add(new Point(-10, 0));
			regionPointsB.add(new Point(-10, 10));
			regionPointsB.add(new Point(0, 10));
			regionPointsB.add(new Point(0, 0));
			Region regionB = new Region(regionPointsB);
			System.out.println(regionB);
			// 合并面
			Region newRegion = regionA.union(regionB);
			System.out.println(newRegion);
		} else if ("erase".equals(test)) {
			// 面A
			List<Point> regionPointsA = new ArrayList<Point>();
			regionPointsA.add(new Point(0, 0));
			regionPointsA.add(new Point(10, 0));
			regionPointsA.add(new Point(10, 10));
			regionPointsA.add(new Point(0, 10));
			regionPointsA.add(new Point(0, 0));
			Region regionA = new Region(regionPointsA);
			System.out.println(regionA);
			// 面B
			List<Point> regionPointsB = new ArrayList<Point>();
			regionPointsB.add(new Point(5, 5));
			regionPointsB.add(new Point(-10, 5));
			regionPointsB.add(new Point(-10, -10));
			regionPointsB.add(new Point(5, -10));
			regionPointsB.add(new Point(5, 5));
			Region regionB = new Region(regionPointsB);
			System.out.println(regionB);
			// 擦除面
			Region newRegion = regionA.erase(regionB);
			System.out.println(newRegion);
		}
	}

}
